﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.IO;
using Newtonsoft.Json;

namespace aaAttributeBrowser
{
    public partial class aaAttributeBrowserControl: UserControl
    {
        private string _objectName = "";
        private bool _showObjectName = false;
        private System.Threading.Timer _t;
        private bool _stopTimer = false;
        private bool _triggerResizeNextTime = false;

        public String Server { get; set; }
        public int MaxWidth { get; set; }
        public int MaxHeight { get; set; }
        public bool AutoSizeParent { get; set; }
        /// <summary>
        /// Defines if the attribute name column is shown on startup
        /// </summary>
        public bool ShowAttributeName { get; set; } = false;
        /// <summary>
        /// Defines if the attribute description is shown on startup
        /// </summary>
        public bool ShowAttributeDescription { get; set; } = true;

        public event EventHandler<ArchestrAResizeEventArgs> ArchestrAResize;

        protected virtual void OnRaiseArchestrAResize(ArchestrAResizeEventArgs e)
        {
            ArchestrAResize?.Invoke(this, e);
        }

        /// <summary>
        /// Prepend the object name to the attribute in the ListView
        /// </summary>
        public bool ShowObjectName
        {
            get
            {
                return _showObjectName;
            }
            set
            {
                _showObjectName = value;
                foreach (ListViewItem lvi in listView1.Items)
                {
                    if (_showObjectName)
                    {
                        if (!lvi.SubItems[0].Text.StartsWith(_objectName))
                        {
                            lvi.SubItems[0].Text = _objectName + "." + lvi.SubItems[0].Text;
                        }
                    }
                    else
                    {
                        if (lvi.SubItems[0].Text.StartsWith(_objectName))
                        {
                            lvi.SubItems[0].Text = lvi.SubItems[0].Text.Substring(_objectName.Length+1,lvi.SubItems[0].Text.Length - _objectName.Length-1);
                        }
                    }
                }
            }
        }

        public aaAttributeBrowserControl()
        {
            InitializeComponent();
            //this is to prevent flicker on update
            listView1.DoubleBuffered(true);
            Server = "http://127.0.0.1:8123/";
            MaxWidth = 1000;
            MaxHeight = 800;
        }

        /// <summary>
        /// Get attributes now, and then every two seconds.
        /// </summary>
        /// <param name="ObjectName"></param>
        public void GetNewAttributes(string ObjectName)
        {
            _stopTimer = false;
            listView1.Items.Clear();
            _objectName = ObjectName;
            _t = new System.Threading.Timer(t_Tick, null, 0, System.Threading.Timeout.Infinite);
            Loading();
        }

        /// <summary>
        /// Request object attributes from server, and display in the list
        /// </summary>
        /// <param name="ObjectName"></param>
        public void GetAttributes(string ObjectName)
        {
            try
            {
                if (_stopTimer)
                    return;
                _objectName = ObjectName;
                var request = WebRequest.Create(Server + "object/" + ObjectName);
                string result = new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd();
                bool triggerResizeNewItems = false;
                bool triggerResizeInitData = _triggerResizeNextTime;
                _triggerResizeNextTime = false;


                this.BeginInvoke(new Action(() =>
                {
                    aaObject obj = JsonConvert.DeserializeObject<aaObject>(result);
                    if (obj.Attributes.Count == 0)
                    {
                        FinishedLoading(string.Format("Object '{0}' did not have any attributes to display.", obj.Name));
                    }
                    var res = listView1.Items.Cast<ListViewItem>();
                    //TODO: I think we can do this better
                    foreach (aaAttribute attrib in obj.Attributes)
                    {
                        var lvi = from t in res where t.Tag.ToString() == attrib.Name select t;
                        if (lvi.Count() == 0)
                        {
                            //we are adding a new item, so trigger a resize event
                            triggerResizeNewItems = true;
                            ListViewItem lviNew = new ListViewItem(attrib.Name);
                            lviNew.Tag = attrib.Name;
                            if (attrib.Value == "Initializing...")
                            {
                                _triggerResizeNextTime = true;
                            }
                            lviNew.SubItems.Add(attrib.Value);
                            lviNew.SubItems.Add(attrib.Description);
                            listView1.Items.Add(lviNew);
                            if (attrib.InAlarm)
                            {
                                lviNew.BackColor = Color.Salmon;
                            }
                            else
                            {
                                lviNew.BackColor = Color.White;
                            }
                            
                        }
                        else
                        {
                            var item = lvi.First();
                            if (attrib.Value == "Initializing...")
                            {
                                _triggerResizeNextTime = true;
                            }
                            item.SubItems[1].Text = attrib.Value;
                            item.SubItems[2].Text = attrib.Description;
                            if (attrib.InAlarm)
                            {
                                item.BackColor = Color.Salmon;
                            }
                            else
                            {
                                if (attrib.Quality != 192)
                                {
                                    item.BackColor = Color.LightYellow;
                                }
                                else
                                {
                                    item.BackColor = Color.White;
                                }
                            }
                            
                        }
                    }
                    if ((triggerResizeNewItems && !_triggerResizeNextTime) || triggerResizeInitData )
                    {
                        if (columnHeader1.Width != 0)
                            columnHeader1.Width = -2;
                        if (columnHeader2.Width != 0)
                            columnHeader2.Width = -2;
                        if (columnHeader3.Width != 0)
                            columnHeader3.Width = -2;
                        OnRaiseArchestrAResize(new aaAttributeBrowser.ArchestrAResizeEventArgs(GetWidth(), GetHeight()));
                        if (AutoSizeParent)
                        {
                            this.ParentForm.Width = GetWidth();
                            this.ParentForm.Height = GetHeight();
                        }
                        FinishedLoading();
                    }
                    
                }));
                if (!_stopTimer)
                    _t.Change(2000, System.Threading.Timeout.Infinite);

            }
            catch (Exception ex)
            {
                //TODO: process other types of error (unable to reach server etc)
                //if we get a 404, then stop requesting
                FinishedLoading(ex.Message.ToString());
            }
        }
        
        /// <summary>
        /// Kill the Update Timer (if running) when the control is destroyed
        /// </summary>
        /// <param name="e"></param>
        protected override void OnHandleDestroyed(EventArgs e)
        {
            base.OnHandleDestroyed(e);
            StopTimer();
        }

        private void listView1_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                mnuPopup.Show(PointToScreen(e.Location));
            }
        }

        private void mnuShowObjectName_Click(object sender, EventArgs e)
        {
            mnuShowObjectName.Checked = !mnuShowObjectName.Checked;
            ShowObjectName = mnuShowObjectName.Checked;
        }
        
        private void t_Tick(object arg)
        {
            GetAttributes(_objectName);
        }
        public void StopTimer()
        {
            _stopTimer = true;
        }

        public int GetHeight()
        {
            int height = 0;
            if (listView1.Items.Count > 0)
                height = listView1.GetItemRect(0).Height * (listView1.Items.Count + 4);
            if (height > MaxHeight)
                height = MaxHeight;
            return Height < height ? height : Height;
        }

        public int GetWidth()
        {
            int width = columnHeader1.Width + columnHeader2.Width + columnHeader3.Width + 20;
            if (width > MaxWidth)
                width = MaxWidth;
            return Width < width ? width : Width;
        }

        public Form GetParent()
        {
            return this.ParentForm;
        }

        private void Loading()
        {
            this.Invoke((MethodInvoker)delegate {
                listView1.Enabled = false;
                pnlLoading.Visible = true;
            });
            txtStatusLabel.Text = "";
            
        }

        private void FinishedLoading(string message = "")
        {
            this.Invoke((MethodInvoker)delegate {
                listView1.Enabled = true;
                pnlLoading.Visible = false;
                txtStatusLabel.Text = message;
            });
        }

        private void pnlLoading_VisibleChanged(object sender, EventArgs e)
        {
            if (pnlLoading.Visible)
            {
                loadingCircle1.Active = true;
            }
            else
            {
                loadingCircle1.Active = false;
            }
        }

        private void mnuAttributeName_Click(object sender, EventArgs e)
        {
            mnuAttributeName.Checked = !mnuAttributeName.Checked;
            if (mnuAttributeName.Checked)
            {
                listView1.Columns[0].Width = -2;
            }
            else
            {
                listView1.Columns[0].Width = 0;
            }
        }

        private void mnuAttributeDescription_Click(object sender, EventArgs e)
        {
            mnuAttributeDescription.Checked = !mnuAttributeDescription.Checked;
            if (mnuAttributeDescription.Checked)
            {
                listView1.Columns[2].Width = -2;
            }
            else
            {
                listView1.Columns[2].Width = 0;
            }
        }

        private void aaAttributeBrowserControl_Load(object sender, EventArgs e)
        {
            if (!ShowAttributeDescription)
            {
                mnuAttributeDescription.Checked = false;
                listView1.Columns[2].Width = 0;
            }
            if (!ShowAttributeName)
            {
                mnuAttributeName.Checked = false;
                listView1.Columns[0].Width = 0;
            }

        }
    }
}
